pub mod ser;
pub mod de;
mod error;

use std::{borrow::Borrow, io, mem, ops::Deref, slice::Iter};

use byteorder::{BigEndian, ByteOrder};

#[repr(u8)]
#[derive(Clone, Copy)]
pub enum ValueType {
    Nil = 0,
    Boolean,
    Integer,
    Real,
    Bytes,
    Ident,
    String,
    Pair,
    Compound,
    List,
    Max,
}

impl ValueType {
    pub fn read_bin<R: io::Read>(r: &mut R) -> io::Result<Self> {
        let mut buf = [0; 1];
        r.read(&mut buf)?;
        if buf[0] >= Self::Max as u8 {
            Err(io::Error::new(io::ErrorKind::Other, "Unrecognized type."))
        } else {
            Ok(unsafe { mem::transmute(buf[0]) })
        }
    }

    pub fn write_bin<W: io::Write>(&self, w: &mut W) -> io::Result<usize> {
        w.write(&[*self as u8])
    }
}

#[derive(Debug, Clone)]
pub enum Value {
    Nil,
    Boolean(bool),
    Integer(i32),
    Real(f32),
    Bytes(Vec<u8>),
    Ident(String),
    String(String),
    Pair(Box<Value>, Box<Value>),
    Compound(Vec<Value>),
    List(Vec<Value>),
}

impl Value {
    pub fn boolean(b: bool) -> Self {
        Self::Boolean(b)
    }

    pub fn integer(i: i32) -> Self {
        Self::Integer(i)
    }

    pub fn real(r: f32) -> Self {
        Self::Real(r)
    }

    pub fn bytes(b: impl Into<Vec<u8>>) -> Self {
        Self::Bytes(b.into())
    }

    pub fn ident(s: impl Into<String>) -> Self {
        Self::Ident(s.into())
    }

    pub fn string(s: impl Into<String>) -> Self {
        Self::String(s.into())
    }

    pub fn pair(k: Value, v: Value) -> Self {
        Self::Pair(Box::new(k), Box::new(v))
    }

    pub fn list(l: impl Into<Vec<Value>>) -> Self {
        Self::List(l.into())
    }

    pub fn compound(l: impl Into<Vec<Value>>) -> Self {
        Self::Compound(l.into())
    }

    pub fn is_pair(&self) -> bool {
        if let Self::Pair(_, _) = self {
            return true;
        }
        false
    }

    pub fn get_key(&self) -> Option<&str> {
        if let Self::Pair(k, _) = self {
            if let Self::Ident(id) = k.deref() {
                return Some(&id);
            }
        }
        None
    }

    pub fn get_iter(&self) -> Option<Iter<'_, Value>> {
        match self {
            Self::List(l) => Some(l.iter()),
            Self::Compound(c) => Some(c.iter()),
            _ => None,
        }
    }

    pub fn read_bin<R: io::Read>(r: &mut R) -> io::Result<Self> {
        match ValueType::read_bin(r)? {
            ValueType::Nil => Ok(Self::Nil),
            ValueType::Boolean => {
                let mut buf = [0; 1];
                r.read(&mut buf)?;
                Ok(Self::boolean(buf[0] != 0))
            }
            ValueType::Integer => {
                let mut buf = [0; 4];
                r.read(&mut buf)?;
                Ok(Self::integer(BigEndian::read_i32(&buf)))
            }
            ValueType::Real => {
                let mut buf = [0; 4];
                r.read(&mut buf)?;
                Ok(Self::real(BigEndian::read_f32(&buf)))
            }
            ValueType::Bytes => {
                let mut buf = vec![0; 8];
                r.read(&mut buf)?;
                buf.resize(BigEndian::read_u64(&buf) as _, 0);
                r.read(&mut buf)?;
                Ok(Self::bytes(buf))
            }
            ValueType::Ident => {
                let mut buf = vec![0; 1];
                r.read(&mut buf)?;
                let len = buf[0] as usize;
                buf.resize(len, 0);
                r.read(&mut buf)?;
                if let Ok(s) = String::from_utf8(buf) {
                    Ok(Self::ident(s))
                } else {
                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Ident is not in UTF-8",
                    ))
                }
            }
            ValueType::String => {
                let mut buf = vec![0; 8];
                r.read(&mut buf)?;
                buf.resize(BigEndian::read_u64(&buf) as _, 0);
                r.read(&mut buf)?;
                if let Ok(s) = String::from_utf8(buf) {
                    Ok(Self::string(s))
                } else {
                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        "String is not in UTF-8",
                    ))
                }
            }
            ValueType::Pair => {
                let k = Self::read_bin(r)?;
                let v = Self::read_bin(r)?;
                Ok(Self::pair(k, v))
            }
            ValueType::Compound => {
                let mut buf = [0; 1];
                r.read(&mut buf)?;
                let len = buf[0] as u8;
                let mut compound = Vec::new();
                for _ in 0..len {
                    compound.push(Self::read_bin(r)?);
                }
                Ok(Self::Compound(compound))
            }
            ValueType::List => {
                let mut buf = [0; 8];
                r.read(&mut buf)?;
                let len = BigEndian::read_u64(&buf);
                let mut list = Vec::new();
                for _ in 0..len {
                    list.push(Self::read_bin(r)?);
                }
                Ok(Self::list(list))
            }
            _ => Err(io::Error::new(io::ErrorKind::Other, "Unrecognized type.")),
        }
    }

    pub fn write_bin<W: io::Write>(&self, w: &mut W) -> io::Result<usize> {
        match self {
            Value::Nil => ValueType::Nil.write_bin(w),
            Value::Boolean(boolean) => {
                Ok(ValueType::Boolean.write_bin(w)? + w.write(&[*boolean as u8])?)
            }
            Value::Integer(integer) => {
                let mut buf = [0; 4];
                BigEndian::write_i32(&mut buf, *integer);

                Ok(ValueType::Integer.write_bin(w)? + w.write(&buf)?)
            }
            Value::Real(real) => {
                let mut buf = [0; 4];
                BigEndian::write_f32(&mut buf, *real);

                Ok(ValueType::Real.write_bin(w)? + w.write(&buf)?)
            }
            Value::Bytes(bytes) => {
                let mut buf = [0; 8];
                BigEndian::write_u64(&mut buf, bytes.len() as _);

                Ok(ValueType::Bytes.write_bin(w)? + w.write(&buf)? + w.write(&bytes)?)
            }
            Value::Ident(id) => Ok(ValueType::Ident.write_bin(w)?
                + w.write(&[id.len() as u8])?
                + w.write(&id.as_bytes())?),
            Value::String(s) => {
                let mut buf = [0; 8];
                BigEndian::write_u64(&mut buf, s.len() as _);

                Ok(ValueType::String.write_bin(w)? + w.write(&buf)? + w.write(&s.as_bytes())?)
            }
            Value::Pair(k, v) => {
                Ok(ValueType::Pair.write_bin(w)? + k.write_bin(w)? + v.write_bin(w)?)
            }
            Value::Compound(vec) => {
                let mut len = 0;

                len += ValueType::Compound.write_bin(w)?;
                len += w.write(&[vec.len() as u8])?;
                for val in vec {
                    len += val.write_bin(w)?;
                }
                Ok(len)
            }
            Value::List(vec) => {
                let mut len = 0;

                len += ValueType::List.write_bin(w)?;

                let mut buf = [0; 8];
                BigEndian::write_u64(&mut buf, vec.len() as _);
                len += w.write(&buf)?;
                for val in vec {
                    len += val.write_bin(w)?;
                }
                Ok(len)
            }
        }
    }

    pub fn read_bytes(&self) -> Option<Self> {
        if let Self::Bytes(bytes) = self {
            Self::read_bin(&mut bytes.as_slice()).ok()
        } else {
            None
        }
    }

    pub fn clone_child(&self, child_name: impl Borrow<str>) -> Value {
        if let Some(mut iter) = self.get_iter() {
            return iter
                .find(|val| {
                    val.get_key()
                        .map(|id| id == child_name.borrow())
                        .unwrap_or(false)
                })
                .map(|pair| {
                    if let Self::Pair(_, v) = pair {
                        v.deref().clone()
                    } else {
                        panic!()
                    }
                })
                .unwrap_or(Value::Nil);
        }

        Value::Nil
    }

    pub fn clone_child_recursive(&self, child_path: impl Borrow<str>) -> Value {
        let path: &str = child_path.borrow();
        let nodes = path.split('/');
        let mut i = self;
        for node in nodes {
            i = if let Some(Self::Pair(_, v)) = i
                .get_iter()
                .map(|mut iter| {
                    iter.find(|val| val.get_key().map(|id| id == node).unwrap_or(false))
                })
                .flatten()
            {
                v.deref()
            } else {
                return Value::Nil;
            }
        }

        i.clone()
    }
}

impl From<bool> for Value {
    fn from(value: bool) -> Self {
        Self::Boolean(value)
    }
}

impl From<i32> for Value {
    fn from(value: i32) -> Self {
        Self::integer(value)
    }
}

impl From<f32> for Value {
    fn from(value: f32) -> Self {
        Self::real(value)
    }
}

impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Self::ident(value)
    }
}

impl From<String> for Value {
    fn from(value: String) -> Self {
        Self::string(value)
    }
}

impl<V: Into<Value>> From<Vec<V>> for Value {
    fn from(value: Vec<V>) -> Self {
        Self::list(value.into_iter().map(|v| v.into()).collect::<Vec<_>>())
    }
}

impl<V: Into<Value>, const N: usize> From<[V; N]> for Value {
    fn from(value: [V; N]) -> Self {
        Self::compound(value.into_iter().map(|v| v.into()).collect::<Vec<_>>())
    }
}

#[macro_export]
macro_rules! list {
    [$($e: expr), +] => {
        {
            use djon::Value;

            Value::list(vec![$($e,) +])
        }
    };
    [] => {
        {
            use djon::Value;

            Value::list(vec![])
        }
    }
}

#[macro_export]
macro_rules! compound {
    ($($e: expr), +) => {
        {
            use djon::Value;

            Value::compound(vec![$($e,) +])
        }
    };
    () => {
        {
            use djon::Value;

            Value::compound(vec![])
        }
    }
}

pub fn nil() -> Value {
    Value::Nil
}

pub fn boolean(v: bool) -> Value {
    Value::Boolean(v)
}

pub fn integer(v: i32) -> Value {
    Value::Integer(v)
}

pub fn real(r: f32) -> Value {
    Value::Real(r)
}

pub fn bytes(b: impl Into<Vec<u8>>) -> Value {
    Value::Bytes(b.into())
}

pub fn ident(s: impl Into<String>) -> Value {
    Value::Ident(s.into())
}

pub fn string(s: impl Into<String>) -> Value {
    Value::String(s.into())
}

pub fn pair(k: Value, v: Value) -> Value {
    Value::Pair(Box::new(k), Box::new(v))
}

pub fn kvpair(k: impl Into<String>, v: Value) -> Value {
    pair(Value::Ident(k.into()), v)
}